#include <QPair>
#include <QDebug>
#include <QTime>
#include <QFile>
#include "expressionsimplifier.h"
#include "sentence.h"
#include "literals/word.h"
#include "literals/delta.h"
#include "literals/bracket.h"
#include "configuration.h"
#include "logger.h"

ExpressionSimplifier::ExpressionSimplifier(bool fileLogging, const QString &configPath)
  : _config(new Configuration(_translator)) // we are giving him translator, so that it will be configured inside Configuration
  {
  if (fileLogging)
    Logger::instance().logToFile("nca.log");
  else
    Logger::instance().logToStdout();
  connect(&Logger::instance(), &Logger::error, this, &ExpressionSimplifier::error, Qt::QueuedConnection);

  qDebug() << "-----------------------------------------------------";
  if (_config->loadXML(configPath) == false)
    qCritical() << "Could not open config file!" << configPath;
  }

ExpressionSimplifier::~ExpressionSimplifier()
  {
  }
    
bool ExpressionSimplifier::loadConfig(QString path)
  {
  return _config->loadXML(path);
  }

QStringList ExpressionSimplifier::simplify()
  {
  bool isThereStillWorkToDo = true;
  QStringList qsl;

  while (isThereStillWorkToDo)
    {
    isThereStillWorkToDo = multiplyAllBrackets();
    isThereStillWorkToDo |= simplifyDeltas(_config->treatLabelsAsNumbers());
    isThereStillWorkToDo |= sameSentencesArithmetic();
    isThereStillWorkToDo |= doOneStep();
    qsl << _translator.expToString(_exp);
    }

  factorOut();
  _exp.sort();

  qsl << _translator.expToString(_exp);
  qsl.removeDuplicates();
  return qsl;
  }

// @args if numbers is true, we try to treat labels as numbers
bool ExpressionSimplifier::simplifyDeltas(bool numbers)
  {
  bool changed = false;
  if (numbers)
    changed = simplifyNumberDeltas();

  changed |= _exp.groupTypes();
  changed |= _exp.doDeltaTricks();
  return changed;
  }

/*
 * This function does two things (with assumption that i and j are numbers):
 * if (i == j) ==> delta(i,j) = 1
 * else delta(i,j) = 0
 * This function returns true if expression has been changed
 */
bool ExpressionSimplifier::simplifyNumberDeltas()  // TODO: think of a better name
  {
  int deltaCount = _exp.countWordType(Word::WT_DELTA);
  if (0 == deltaCount )
    return false;
  bool changed = false;
  bool isNumber0, isNumber1; //check if label is number
  float label1, label2;
  //simplify deltas
  for ( int counter = 0; counter < deltaCount; counter++)
    {
    for (size_t i = 0; i < _exp.size(); i++)
      {
      for (int j = 0; j < _exp.at(i).size(); j++) //foreach word
        {
        if ((_exp.at(i).at(j)->getType() == Word::WT_DELTA) && (_exp.at(i).at(j)->getLabels().size() >= 2))
          {
          label1 = _exp.at(i).at(j)->getLabels()[0].toFloat(&isNumber0);
          label2 = _exp.at(i).at(j)->getLabels()[1].toFloat(&isNumber1);
          if (isNumber0 & isNumber1) // labels are numbers
            {
            changed = true;
            if (label1 == label2) // whole delta is equal to one
              _exp.removeWords(i,j,1);
            else // whole delta is equal to zero, so whole current sentence is removed
              {
              _exp.erase(_exp.begin() + i);
              break;
              }
            }
          }
        }
      }
    }
  return changed;
  }

/*
 * issue 29: Arithmetic of same sentences
 * @brief This function recognize same sentences and adds them according to their coefficients.
 * I.e:       a_i b_j + a_i b_j
 * should be  2 a_i b_j
 * @return true, if expression changed
 *
 * 1. check if the same sentences exist
 * 2. add them according to coefficients
 */
bool ExpressionSimplifier::sameSentencesArithmetic()
  {
  return _exp.sameSentencesArithmetic();
  }

bool ExpressionSimplifier::factorOut()
  {
  return _exp.factorOutWhatYouCan();
  }

/*
 * Getting rid happens by multiplying the expression in the bracket
 * with neighbouring words in the same sentence the bracket is in. *
 */
bool ExpressionSimplifier::multiplyAllBrackets()
  {
  return _exp.multiplyAllBrackets();
  }

bool ExpressionSimplifier::doOneStep()
  {
  QMap<int, Position> foundMacroPatterns = _config->updateMacroRules(_exp);
  int nFoundMacroPatterns = foundMacroPatterns.keys().size() - foundMacroPatterns.keys(Position(-1,-1)).size(); // this might be slow

  if (nFoundMacroPatterns)
    nFoundMacroPatterns = findAndApply(_config->generatedRules());
  if (!nFoundMacroPatterns)
    nFoundMacroPatterns = findAndApply(_config->rules());

  return nFoundMacroPatterns;
  }

bool ExpressionSimplifier::findAndApply(const QList<Rule> &rules)
  {
  if (rules.size() < 1)
    return false;

  QPair<int, int> position(-1, -1);
  int &sentenceIndex = position.first;

  int i; // we need it to remember which rule pattern was found
  for (i = 0; i < rules.size(); i++)
    {
    position = rules.at(i).findPatternInExp(_exp);  // returns -1,-1 if doesn't find anything
    if (sentenceIndex != -1)
      break;
    }

  if (sentenceIndex == -1)
    return false;

  qDebug() << "Applying rule:" << _translator.expToString(rules.at(i).key()) <<
              "->" << _translator.expToString(rules.at(i).value()) <<
              "to" << _translator.expToString(_exp) << "at" << position;
  rules.at(i).apply(_exp, position);

  return true;
  }

void ExpressionSimplifier::setExpFromString(const QString& input)
  {
  _exp.clear();
  _translator.stringToExp(input.simplified(), _exp);
  qDebug() << "Expression set to" << _translator.expToString(_exp);
  }

QString ExpressionSimplifier::getExp()
  {
  return _translator.expToString(_exp);
  }

QString ExpressionSimplifier::getVersion()
  {
  return APP_VERSION;
  }
